﻿using Dapper;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;

namespace Gaea
{
    /// <summary>
    /// Gaea 驱动基类   (异步部分）
    /// </summary>
    public abstract partial class GaeaBasicPower : IGaeaPower
    {
        #region 私有字段
        /// <summary>
        /// Connection Factory
        /// </summary>
        private IGaeaConnectionFactory _connectionFactory;

        #endregion

        #region 构造函数
        /// <summary>
        /// 无参构造函数
        /// </summary>
        public GaeaBasicPower()
        {
            var scope = GaeaExtension.ServiceProviderRoot.CreateScope();
            _connectionFactory = scope.ServiceProvider.GetRequiredService<IGaeaConnectionFactory>();
        }
        #endregion

        /// <summary>
        /// 获取类型在数据库的表名映射
        /// </summary>
        /// <typeparam name="T">GaeaSon 继承子类</typeparam>
        /// <returns></returns>
        public string CallGaeaName<T>() where T : IGaeaSon
        {
            return GaeaSuperPower.BuildGaeaName<T>(this);
        }

        /// <summary>
        /// 获取类型在数据库的表名映射
        /// </summary>
        /// <typeparam name="TGaeaBasicPower">TGaeaBasicPower 继承子类</typeparam>
        /// <typeparam name="TGaeaSon">GaeaSon 继承子类</typeparam>
        /// <returns></returns>
        public static string CallGaeaName<TGaeaBasicPower, TGaeaSon>() where TGaeaBasicPower : GaeaBasicPower where TGaeaSon : IGaeaSon
        {
            return GaeaSuperPower.BuildGaeaName(typeof(TGaeaBasicPower), typeof(TGaeaSon));
        }

        /// <summary>
        /// 构建SQL Connection
        /// </summary>
        /// <returns></returns>
        public virtual async Task<IDbConnection> CreateConnectionAsync()
        {
            return await _connectionFactory.GetConnectionAsync(this);
        }

        /// <summary>
        /// 显示构建 SQL Connection
        /// </summary>
        /// <typeparam name="TDbConnection">DbConnection 子类</typeparam>
        /// <returns></returns>
        public virtual async Task<TDbConnection> CreateConnectionAsync<TDbConnection>() where TDbConnection : class, IDbConnection
        {
            return (await _connectionFactory.GetConnectionAsync(this)) as TDbConnection;
        }

        /// <summary>
        /// 查询多行记录
        /// </summary>
        /// <typeparam name="T">返回结果映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>对应类型结果集</returns>
        public virtual async Task<IEnumerable<T>> FindAsync<T>(string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null) where T : class
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                return await FindAsync<T>(dbConnection, commandText, parameters, commandType, commandTimeout);
            }
        }

        /// <summary>
        /// 查询多行记录
        /// </summary>
        /// <typeparam name="T">返回结果映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>对应类型结果集</returns>
        public virtual async Task<IEnumerable<T>> FindAsync<T>(IDbConnection dbConnection, string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null) where T : class
        {
            var result = await dbConnection.QueryAsync<T>(commandText, parameters, null, commandTimeout, commandType);
            dbConnection.Close();
            return result;
        }


        /// <summary>
        /// 查询单条记录（如果查询记录出现多条则抛出异常）
        /// </summary>
        /// <typeparam name="T">返回结果映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>结果</returns>
        public virtual async Task<T> FindOneAsync<T>(string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null) where T : class
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await dbConnection.QuerySingleOrDefaultAsync<T>(commandText, parameters, null, commandTimeout, commandType);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 查询单条记录（如果查询记录出现多条则抛出异常）多用于事务
        /// </summary>
        /// <typeparam name="T">返回结果映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>结果</returns>
        public virtual async Task<T> FindOneAsync<T>(IDbConnection dbConnection, string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null) where T : class
        {
            var result = await dbConnection.QuerySingleOrDefaultAsync<T>(commandText, parameters, null, commandTimeout, commandType);
            return result;
        }

        /// <summary>
        /// 执行SQL返回第一行第一列结果
        /// </summary>
        /// <typeparam name="T">返回结果映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>第一行第一列结果</returns>
        public virtual async Task<T> ExecuteScalarAsync<T>(string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null)
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await ExecuteScalarAsync<T>(dbConnection, commandText, parameters, commandType, commandTimeout);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 执行SQL返回第一行第一列结果 多用于事务
        /// </summary>
        /// <typeparam name="T">返回结果映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>第一行第一列结果</returns>
        public virtual async Task<T> ExecuteScalarAsync<T>(IDbConnection dbConnection, string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null)
        {
            return await dbConnection.ExecuteScalarAsync<T>(commandText, parameters, null, commandTimeout, commandType);
        }

        /// <summary>
        /// 执行SQL返回影响行数
        /// </summary>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> ExecuteAsync(string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null)
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await ExecuteAsync(dbConnection, commandText, parameters, commandType, commandTimeout);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 执行SQL返回影响行数 多用于事务
        /// </summary>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">参数</param>
        /// <param name="commandType">T-SQL语句类型</param>
        /// <param name="commandTimeout">执行超时时间</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> ExecuteAsync(IDbConnection dbConnection, string commandText, object parameters, CommandType commandType = CommandType.Text, int? commandTimeout = null)
        {
            return await dbConnection.ExecuteAsync(commandText, parameters, null, commandTimeout, commandType);
        }

        /// <summary>
        /// 返回所有结果
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        [Obsolete("之后会进行成重构")]
        public virtual async Task<IEnumerable<T>> FindAllAsync<T>() where T : class, IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await dbConnection.FindAllAsync<T>(this);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 返回所有结果
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sort"></param>
        /// <param name="sortField"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        [Obsolete("之后会进行成重构")]
        public virtual async Task<IEnumerable<T>> FindAllAsync<T>(GaeaSort sort = GaeaSort.ASC, string sortField = "id", int? commandTimeout = null) where T : IGaeaSon
        {
            var commandText = $"SELECT * FROM {CallGaeaName<T>()}";
            if (sort == GaeaSort.ASC)
            {
                commandText = $"{commandText} ORDER BY {sortField}";
            }
            else
            {
                commandText = $"{commandText} ORDER BY {sortField} DESC";
            }
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await dbConnection.QueryAsync<T>(commandText, commandTimeout: commandTimeout);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// Id 查询出对应结果并映射
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="id">内部唯一编号</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <returns>映射类型</returns>
        public virtual async Task<T> FindByIdAsync<T>(int id, int? commandTimeout = null) where T : class
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await FindByIdAsync<T>(dbConnection, id, commandTimeout);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        ///  Id 查询出对应结果并映射 多用于事务
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="id">内部编号</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <returns>映射类型</returns>
        public virtual async Task<T> FindByIdAsync<T>(IDbConnection dbConnection, int id, int? commandTimeout = null) where T : class
        {
            return await dbConnection.FindByIdAsync<T>(this, id);
        }

        /// <summary>
        /// 类型映射 数据插入操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="son">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> InsertAsync<T>(T son) where T : class, IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await InsertAsync(dbConnection, son);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 类型映射 数据插入操作 多用于事务
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="son">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> InsertAsync<T>(IDbConnection dbConnection, T son) where T : class, IGaeaSon
        {
            return await dbConnection.InsertAsync(this, son);
        }

        /// <summary>
        /// 类型映射 数据库更新操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="son">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> UpdateAsync<T>(T son) where T : class, IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await UpdateAsync(dbConnection, son);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 类型映射 数据库更新操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="son">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> UpdateAsync<T>(IDbConnection dbConnection, T son) where T : class, IGaeaSon
        {
            return await dbConnection.UpdateAsync(this, son);
        }

        /// <summary>
        /// 批量数据插入操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="sons">实例集合</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> BatchInsertAsync<T>(string commandText, IEnumerable<T> sons) where T : IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await BatchInsertAsync(dbConnection, commandText, sons);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 批量数据插入操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="commandText">T-SQL</param>
        /// <param name="sons">实例集合</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> BatchInsertAsync<T>(IDbConnection dbConnection, string commandText, IEnumerable<T> sons) where T : IGaeaSon
        {
            return await dbConnection.ExecuteAsync(commandText, sons);
        }

        /// <summary>
        /// 批量数据更新操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="sons">实例集合</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> BatchUpdateAsync<T>(string commandText, IEnumerable<T> sons) where T : IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await dbConnection.ExecuteAsync(commandText, sons);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 批量数据更新操作
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="sons">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> DeleteAsync<T>(T son) where T : class, IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await DeleteAsync(dbConnection, son);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 删除数据操作 多用于事物
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="dbConnection">T-SQL</param>
        /// <param name="son">实例</param>
        /// <returns></returns>
        public virtual async Task<int> DeleteAsync<T>(IDbConnection dbConnection, T son) where T : class, IGaeaSon
        {
            return await dbConnection.DeleteAsync(this, son, null);
        }

        /// <summary>
        /// 删除所有数据 请谨慎使用
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <returns>影响行数</returns>
        public virtual async Task<int> DeleteAllAsync<T>() where T : class, IGaeaSon
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var result = await dbConnection.DeleteAllAsync<T>(this);
                dbConnection.Close();
                return result;
            }
        }

        /// <summary>
        /// 编辑 如果GaeaSon实例Id 大于零Update 为零则Insert
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="son">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> ModifyAsync<T>(T son) where T : class, IGaeaSon
        {
            if (son.Id <= 0)
            {
                return await InsertAsync(son);
            }
            else
            {
                return await UpdateAsync(son);
            }
        }

        /// <summary>
        /// 编辑 如果GaeaSon实例Id 大于零Update 为零则Insert 多用于事物
        /// </summary>
        /// <typeparam name="T">映射类型</typeparam>
        /// <param name="dbConnection">Db Connection</param>
        /// <param name="son">实例</param>
        /// <returns>影响行数</returns>
        public virtual async Task<int> ModifyAsync<T>(IDbConnection dbConnection, T son) where T : class, IGaeaSon
        {
            if (son.Id <= 0)
            {
                return await InsertAsync(dbConnection, son);
            }
            else
            {
                return await UpdateAsync(dbConnection, son);
            }
        }

        /// <summary>
        /// 两组返回数据结果匹配 
        /// </summary>
        /// <typeparam name="TFirst">第一组类型映射</typeparam>
        /// <typeparam name="TSecond">第二组类型映射</typeparam>
        /// <param name="commandText">T-SQL</param>
        /// <param name="parameters">具体参数</param>
        /// <returns>两组结果集合</returns>
        public virtual async Task<Tuple<IEnumerable<TFirst>, IEnumerable<TSecond>>> FindMultipleAsync<TFirst, TSecond>(string commandText, object parameters) where TFirst : class
        {
            using (var dbConnection = await CreateConnectionAsync())
            {
                var multipleResult = await dbConnection.QueryMultipleAsync(commandText, parameters);

                var result = new Tuple<IEnumerable<TFirst>, IEnumerable<TSecond>>(
                    await multipleResult.ReadAsync<TFirst>(), await multipleResult.ReadAsync<TSecond>());

                dbConnection.Close();
                return result;
            }
        }
    }
}
